# -*- coding: utf-8 -*-
# vim: set noet ts=4:
#
# scim-python
#
# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
#
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA
#
# $Id: $
#
import os, sys
import sqlite3 as sqlite
import traceback
import PYUtil
import re
import PYParser

(YLEN, Y0, Y1, Y2, Y3, YX, S0, S1, S2, S3, PHRASE, FREQ) = range (0, 12)


class PYSQLiteDB:
	"""phrase database that contains all phrases and phrases' pinyin"""
	def __init__ (self, name = None):
		if not name:
			name = os.path.join (os.path.dirname(__file__), "py.db")
		self.db = sqlite.connect (name)
		self.parser = PYParser.PinYinParser ()
		# self.db.execute ("PRAGMA locking_mode = EXCLUSIVE;")
		# self.db.execute ("PRAGMA synchronous = OFF;")
		self.db.execute ("PRAGMA cache_size = 10000;")
		self.select_cache = Cache ()

	def create_tables (self):
		"""create all phrases tables that contain all phrases"""
		# create pinyin table
		sqlstring = "CREATE TABLE py_pinyin (pinyin TEXT PREMARY KEY);"
		self.db.execute (sqlstring)
		
		# create pinyin table
		sqlstring = "CREATE TABLE py_shengmu (shengmu TEXT PREMARY KEY);"
		self.db.execute (sqlstring)

		# create phrase table
		sqlstring = """CREATE TABLE py_phrase_%(i)d (
							ylen INTEGER, 
							y0 INTEGER, y1 INTEGER, y2 INTEGER, y3 INTEGER, yx TEXT,
							s0 INTEGER, s1 INTEGER, s2 INTEGER, s3 INTEGER,
							phrase TEXT,
							freq INTEGER);
				/*
				CREATE UNIQUE INDEX py_phrase_%(i)d_index_1 on py_phrase_%(i)d (y0, y1, y2, y3, yx, phrase);
				CREATE INDEX py_phrase_%(i)d_index_2 on py_phrase_%(i)d (ylen, freq DESC);
				CREATE INDEX py_phrase_%(i)d_index_4 on py_phrase_%(i)d (s0, s1, s2, s3);
				*/
				"""
		
		for i in range (0, len (PYUtil.SHENGMU_DICT) + 1):
			self.db.executescript (sqlstring % {"i":i})
		
		self.db.commit ()

	def init_pinyin_table (self):
		"""create table pinyin that contains all pinyin"""
		sqlstring = "INSERT INTO py_pinyin (pinyin) VALUES (?)"
		for py in PYUtil.PINYIN_DICT.keys():
			self.db.execute (sqlstring, (unicode (py),))
		self.db.commit ()
	
	def init_shengmu_table (self):
		"""create table shengmu that contains all shengmu of pinyin"""
		sqlstring = "INSERT INTO py_shengmu (shengmu) VALUES (?)"
		for shengmu in PYUtil.SHENGMU_DICT.keys ():
			self.db.execute (sqlstring, (unicode (shengmu),))
		self.db.commit ()

	def add_phrases (self, phrases):
		""" add phrases to database, phrases is a iterable object
		Like: [(phrase, pinyin, freq), (phrase, pinyin, freq), ...]
		"""
		sqlstring = """INSERT INTO py_phrase_%d (ylen, y0, y1, y2, y3, yx, s0, s1, s2, s3, phrase, freq)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"""
		line = 1
		for  phrase, pinyin, freq in phrases:
			try:
				py = self.parser.parse (pinyin)
				if len(py) != len (phrase):
					raise Exception (u"%s %s"%(phrase, pinyin))
				record = [None, None, None, None, None, None, None, None, None, None, None, 0]
				record [YLEN] = len (py)
				i = 0
				for p in py[:4]:
					record[Y0 + i] = p.get_pinyin_id ()
					record[S0 + i] = p.get_sheng_mu_id ()
					i += 1
				if len(py) > 4:
					record[YX] = "'".join (map (str, py[4:]))
				record[PHRASE] = phrase
				record[FREQ] = freq
				self.db.execute (sqlstring % record[S0], record)
			except Exception, e:
				# import traceback
				# traceback.print_exc ()
				print line, ":", phrase.encode ("utf-8"), pinyin, freq
				# print e
			line += 1
			
		self.db.commit ()

	def select_words_by_pinyin_list (self, pys):
		"""select words from database by list that contains PYUtil.PinYinWord objects"""
		result = self.select_cache [u"'".join (map (str, pys))]
		if result != None:
			return result

		sql_prefix = "SELECT phrase FROM py_phrase_%d WHERE" % pys[0].get_sheng_mu_id ()

		sql_conditions = ["ylen = %d" % len (pys)]
		if len (pys) <= 4:
			i = 0
			for py in pys:
				if py.is_valid_pinyin ():
					sql_conditions.append ("y%d = %d" % (i, py.get_pinyin_id ()))
				else:
					sql_conditions.append ("s%d = %d" % (i, py.get_sheng_mu_id ()))
				i += 1
		else:
			i = 0
			for py in pys[:4]:
				if py.is_valid_pinyin ():
					sql_conditions.append ("y%d = %d" % (i, py.get_pinyin_id ()))
				else:
					sql_conditions.append ("s%d = %d" % (i, py.get_sheng_mu_id ()))
				i += 1
			pp = lambda (x): x.get_pinyin() if x.is_valid_pinyin () else x.get_pinyin() + "%"
			pattern = "'".join (map (pp, pys[4:]))
			sql_conditions.append ("yx LIKE \"" + pattern + "\"")

		sql = " ".join ([sql_prefix," AND ".join (sql_conditions), "ORDER BY freq DESC;"])

		result = self.db.execute (sql)

		result = map (lambda x: x[0] ,result)
		self.select_cache [u"'".join (map (str, pys))] = result
		return result
	
	def select_words_by_pinyin_string (self, string):
		"""select words from the database by pinyin string"""
		pys = self.parser.parse (string)
		# print "'".join (map (str, pys))
		return self.select_words_by_pinyin_list (pys)


class Cache:
	"""cache object for cache history queries"""

	class Slot:
		"""Slot item of cache object"""
		def __init__ (self):
			self.time = -1
			self.value = None
			self.index = None
	
	def __init__ (self, max_slot = 1024):
		self._max_slot = max_slot
		self._slots = []
		self._dict = {}
		self._time = 0
	
	def __getitem__ (self, index):
		"""return a vale associate with the giving index. It cache does not has this index,
		it will retrun None."""
		if not self._dict.has_key (index):
			return None
		
		slot = self._dict [index]
		self._time += 1
		slot.time = self._time
		return slot.value

	def __setitem__ (self, index, value):
		if self._dict.has_key (index):
			slot = self._dict[index]
		else:
			slot = self.get_slot ()
		self._time += 1
		slot.value = value
		slot.time = self._time
		slot.index = index
		self._dict[index] = slot

	def get_slot (self):
		"""get_slot will return a empty slot. If there is not any empty slot, 
		it will find the oldest slot and return it."""
		if len(self._slots) < self._max_slot:
			slot = Cache.Slot ()
			self._slots.append (slot)
		else:
			self._slots.sort (lambda x,y : x.time - y.time)		
			slot = self._slots[0]
			del self._dict[slot.index]
		return slot


def test_select_words ():
	import time

	db = PYSQLiteDB ()
	while True:
		l = raw_input ().strip ()
		if not l:
			break
		t = time.time ()
		res = db.select_words_by_pinyin_string (l)
		
		t = time.time () - t
		

		for p in res:
			print p
		print "OK t =", t, " count =", len (res)

	
if __name__ == "__main__":
	test_select_words ()

